#
#  PodLibrary.py
#
# This work is released under the GNU GPL, version 2 or later.
#
from Library import *
from PodPlaylist import *
from PodTrack import *
from PListParser import *
#from LMusicPodMounter import *
#from iTunesDB import *
#from iTunesSD import *
from ipod import *
import backtick
import os

#
# the singleton instance of the iPod library
#
_PodLibrary = None

class PodLibrary(Library):
	def __init__(self):
		Library.__init__(self)
		self.name = "iPod"
		self.dirty = True
		#self.mainPlaylist = PodMasterPlaylist(self)
		#self.addPlaylist(self.mainPlaylist)
		self.mainPlaylist = None
		self.info = None
		self.filePath = None
		self.fileHandle = None
		self.ipod = None

	def markDirty(self,value = True):
		self.dirty = value

	def dbVersion(self):
		version = self.ipod.version
		try: return {9:'4.2',10:'4.5',11:'4.7',12:'4.8',13:'4.9',14:'5.0'}[version]
		except: return "Unknown (code %d)" % version

	def usage(self):
		#values = backtick.backtick("df %s" % self.basePath).split()
		#print "usage",values
		#return (long(values[9])*1024,long(values[10])*1024,long(values[8])*1024)
		(total,free) = self.ipod.diskUsage()
		total *=1024
		free *= 1024
		used = total-free
		return (used,free,total)

	def diskFull(self):
		(used,free,total) = self.usage()
		return (free<=10*1024*1024)
		
	def setOwnerName(self,name):
		self.owner = name
		#self.njb.SetOwnerString(str(name))

	def setDevice(self,basePath):
		if basePath:
			if 1:
				self.basePath = basePath
				#print "PodLibrary(): setDevice opening ipod at path",self.basePath
				self.filePath = os.path.join(self.basePath,"iPod_Control/iTunes/iTunesDB")
				self.ipod = IPod(self.basePath)
				#print "PodLibrary()::setDevice: found",self.ipod,vars(self.ipod),self.ipod.version
				#self.mainPlaylist.setDevice(self.atom)
				self.readStuff()
				self.emit(PYSIGNAL("ipodMounted"),(self,None))
			else:
				print "PodLibrary.setDevice: mount failed"
		else:
			self.ipod = None
			self.emit(PYSIGNAL("ipodUnmounted"),(self,None))
		
	def readStuff(self):
			self.readInfo()
			self.readMusic()
			self.markDirty(False)
	
	def readMusic(self,callback = None,data = None):
			#print "PodLibrary(): reading music"
			self.readTracks(callback,data)
			self.readPlaylists(callback,data)
		
	def readInfo(self):
		pass
	
	def writeStuff(self):
		#self.ipod.flush()
		if self.dirty:
			#print "PodLibrary.writeStuff: writing iPod database version",self.ipod.version
			backupPath = "%s.bak" % self.filePath
			copyFile(self.filePath,backupPath)
			self.ipod.flush()
			self.markDirty(False)
		else:
			print "PodLibrary.writeStuff: database clean"

	#
	# read the playlists from the device
	# note that playlist IDs (particularly the first one) are often renumbered
	#
	def readPlaylists(self,callback = None,data = None):
		playlistCount = self.ipod.playlistCount
		#print "playlists",playlistCount
		incoming = {}
		for playlistIndex in xrange(playlistCount):
			pl = self.ipod.playlistAt(playlistIndex)
			#print vars(pl)
			if callback: callback(i18n("Reading Playlists"),playlistIndex,playlistCount,data)
			#print "PodLibrary: reading playlist",pl
			playlistID = pl.id
			playlist = self.playlistWithID(playlistID)
			if not playlist:
				#print "adding playlist",playlistID,[pl.name()],pl.hidden
				master = pl.hidden
				playlist = self.newPlaylist(master)
				self.addPlaylist(playlist)
				if master: self.mainPlaylist = playlist
			#else: print "already have",[pl.name()]
			playlist.fromPodPlaylist(pl)
			#print "(add) checking", ("%x" % playlist.playlistID),[playlist.name]
			incoming[playlist.playlistID] = 1
		for playlist in self._playlists[:]:
			if not playlist.master:
				#print "(remove) checking",playlist.playlistID,[playlist.name]
				try: incoming[playlist.playlistID]
				except:
					#print "removing",playlist.playlistID,[playlist.name]
					Library.removePlaylist(self,playlist)
	
	def readTracks(self,callback = None,data = None):
		#print "PodLibrary: reading tracks"
		incomingTracks = {}
		songCount = self.ipod.trackCount
		#
		# add any tracks that we haven't seen before
		#
		for songIndex in xrange(songCount):
			song = self.ipod.trackAt(songIndex)
			if callback: callback(i18n("Reading Tracks"),songIndex,songCount,data)
			#print "PodLibrary: reading track",song
			track = PodTrack(self)
			track.fromSong(song)
			if not self.containsTrackID(track.trackID):
				#print "PodLibrary: adding PodTrack",track.trackID
				self.addTrack(track)
			incomingTracks[track.trackID] = track
		#
		# remove any tracks that are now gone
		#
		for track in self.tracks().values():
			try: incomingTracks[track.trackID]
			except:
				#print "PodLibrary: removing PodTrack",track.trackID
				self.removeTrack(track)
		
		#self.updateMainPlaylist()
	
	#
	# update the "main" playlist
	#
	def updateMainPlaylist(self):
		incomingTracks = {}
		for track in self.tracks().values():
			if not self.mainPlaylist.containsTrackID(track.trackID):
				self.mainPlaylist.addTrackID(track.trackID)
			incomingTracks[track.trackID] = 1
		for trackID in self.mainPlaylist.getTrackIDs():
			try: incomingTracks[trackID]
			except: self.mainPlaylist.removeTrackID(trackID)

	def newPlaylist(self,master = False):
		#if master: return PodMasterPlaylist(self)
		return PodPlaylist(self,master)

	#
	# override adding of new playlist to create one on the device
	#
	def addNewPlaylist(self,name = None):
		#print "PodLibrary: adding new playlist"
		playlist = self.newPlaylist()
		pl = IPodPlaylist(self.ipod)
		playlist.fromPodPlaylist(pl)
		if name==None:
			name = "untitled Playlist"
		playlist.setName(name)
		self.markDirty()
		return playlist

	#
	# override to remove the playlist from the device
	#
	def removePlaylist(self,playlist):
		#print "PodLibrary: removing playlist",playlist.playlistID
		for pl in self.ipod.playlists:
			if pl.id==playlist.playlistID:
				pl.remove()
				break
		self.markDirty()
		#self.readPlaylists()

	#
	# see if there's already a track like this one
	#
	def findTrackLike(self,otherTrack):
		otherKey = otherTrack.hashKey()
		for track in self.tracks().values():
			if track.hashKey()==otherKey:
					return track
		return None
	
	def removePodTrackIDFromAllPlaylists(self,trackID):
		for playlist in self.playlists:
			playlist.removePodTrackID(trackID)

	def killTrackID(self,trackID):
		track = self.trackWithTrackID(trackID)
		if track:
			for tr in self.ipod.tracks:
				if tr.id==trackID:
					tr.remove()
					break;
			#tracks = self.atom.songs().songItems()
			#tracks.deleteTrackByID(trackID)
			track.kill()
		
	#
	# prepare for a synchronization between the DDJ and
	# the Lsongs database - create list of tracks and playlists that are either
	# not on the device or Lsongs.  Later on we decide what to do with the
	# items based on the user preferences.
	#
	def getSyncInfoWith(self,library):
		tracksOnPlayerNotInLsongs = self.hashList()
		#print tracksOnPlayerNotInLsongs
		tracksInLsongsNotOnPlayer = {}
		removedTracks = {}
		
		for trackID in library._tracks.keys():
			track = library._tracks[trackID]
			if track.kind.startswith('MPEG'):
				key = track.hashKey()
				#print "track key",[key]
				if tracksOnPlayerNotInLsongs.has_key(key):
					#print "removing track",key
					removedTracks[key] = track
					del tracksOnPlayerNotInLsongs[key]
				else:
					if not tracksInLsongsNotOnPlayer.has_key(key) and not removedTracks.has_key(key): # handle dups
						#print "adding track",key
						tracksInLsongsNotOnPlayer[key] = track
					#else: print "duplicate!"
		#print "still have",tracksOnPlayerNotInLsongs
	
		playlistsOnPlayerNotInLsongs = {}
		for playlist in self.playlists:
			if not playlist.master and not playlist.trash:
				playlistsOnPlayerNotInLsongs[playlist.name] = playlist
		playlistsInLsongsNotOnPlayer = {}
		removedPlaylists = {}
		
		for playlist in library.playlists:
			if not playlist.master and not playlist.trash:
				key = playlist.name
				#print "checking host playlist",[key]
				if playlistsOnPlayerNotInLsongs.has_key(key):
					#print "found on player"
					removedPlaylists[key] = playlist
					del playlistsOnPlayerNotInLsongs[key]
				else:
					if not playlistsInLsongsNotOnPlayer.has_key(key) and not removedPlaylists.has_key(key):
						#print "not found on player"
						playlistsInLsongsNotOnPlayer[key] = playlist
		#print "PodLibrary.sync: adding playlists",[playlistsInLsongsNotOnPlayer],"removing",[playlistsOnPlayerNotInLsongs]
		return (tracksInLsongsNotOnPlayer,tracksOnPlayerNotInLsongs,playlistsInLsongsNotOnPlayer,playlistsOnPlayerNotInLsongs)

	#
	# return the singleton instance of the library corresponding to the iPod
	#
	def singleton():
		global _PodLibrary
		if _PodLibrary==None:
			_PodLibrary = PodLibrary()
		return _PodLibrary
	singleton = staticmethod(singleton)
	
	def mount(basePath = None):
		print "PodLibrary.mount: attempting to mount iPod at",basePath
		if 1: #try:
			lib = PodLibrary.singleton()
			lib.clearTracks()
			if basePath==None:
				basePath = os.path.expanduser('~')
			lib.setDevice(basePath)
		#except: pass
	mount = staticmethod(mount)
	
	def unmount():
		print "PodLibrary.unmount: unmounting iPod"
		lib = PodLibrary.singleton()
		basePath = lib.basePath
		backtick.backtick("umount %s" % basePath)
		backtick.backtick("eject %s" % basePath.replace("mnt","dev"))
		lib.setDevice(None)
	unmount = staticmethod(unmount)

if __name__=="__main__":
	PodLibrary.mount()
	print "done"
